package se.ytterman.jserial.framework.fsm;

import java.lang.*;
import java.util.*;
import java.util.concurrent.*;
import java.io.*;
import java.util.regex.*;

import javax.xml.xpath.*;

import org.w3c.dom.*;

import se.ytterman.jserial.util.*;
import se.ytterman.jserial.framework.device.*;

public class DeviceStateMachine extends Thread {

    public DeviceStateMachine(PriorityBlockingQueue deviceRequestQueue,
                              PriorityBlockingQueue deviceResponseQueue,
                              PriorityBlockingQueue apiRequestQueue,
                              PriorityBlockingQueue apiResponseQueue,
                              RequestFactory requestFactory) {
        // Device request queue
        this.deviceRequestQueue = deviceRequestQueue;
        
        // Device response queue
        this.deviceResponseQueue = deviceResponseQueue;
        
        // API request queue
        this.apiRequestQueue = apiResponseQueue;
        
        // API response queue
        this.apiResponseQueue = apiRequestQueue;
        
        // Device request factory
        this.requestFactory = requestFactory;
        
        // Statemachine dictionary
        this.stateMachines = new Hashtable();
    }
    
    //-------------------------------------------------------------
    // FSM Creation operations
    //-------------------------------------------------------------
    
    public void loadStates(Document fsmModel) {
        try {
            // Extract the events
            XPathFactory xpathFactory = XPathFactory.newInstance();
            XPath fsmExtractor = xpathFactory.newXPath();
                    
            // Iterate over found statemachines
            NodeList stateMachineNodes = (NodeList)fsmExtractor.evaluate("/StateMachines/StateMachine/States", 
                                                                         fsmModel, 
                                                                         XPathConstants.NODESET);
 
            for(int i = 0; i < stateMachineNodes.getLength(); i++) {
                // Extract statemachine 
                Element stateMachineNode = (Element)stateMachineNodes.item(i);
                String machineId = stateMachineNode.getAttribute("id");            
                
                // XXX This may be hazardaous if there are duplicate
                // declarations in the XML description!
                // Fetch statemachine hashtable if present...
                Hashtable machineStates = this.stateMachines.get(machineId);
                
                // ...else create new... 
                if(machineStates == null) {
                    machineStates = new Hashtable();
                }
                
                // DEBUG System.out.println(i + " StateMachine " + machineId);
                
                // Iterate over states
                NodeList machineStateNodes = (NodeList)fsmExtractor.evaluate("State", 
                                                                             stateMachineNode, 
                                                                             XPathConstants.NODESET);
               
                for(int j = 0; j < machineStateNodes.getLength(); j++) {
                    // Extract states
                    Element machineStateNode = (Element)machineStateNodes.item(j);
                    String stateId = machineStateNode.getAttribute("id");
                    String stateClass = machineStateNode.getAttribute("class");
                
                    // DEBUG System.out.println("\t" + j + " MachineState " + stateId + "(" + stateClass + ")");
                                                            
                    // Create new state object
                    StateMachineState stateMachineState = (StateMachineState)InstanceFactory.newInstance(stateClass,  
                                                                                                         false);
                    
                    // Configure state object
                    stateMachineState.configureState(stateMachineNode);
                    
                    // Store newly created state
                    machineStates.put(stateId, stateMachineState);
                }
            }
        }
        catch(Exception e) {
            System.out.println(e);
            e.printStackTrace();
        }
    }
    
    public void loadDependencies(Document fsmModel) {
                
    }
    
    //-------------------------------------------------------------
    // State operations
    //-------------------------------------------------------------  
    
    //-------------------------------------------------------------
    // Transition operations
    //-------------------------------------------------------------
    
    public void run() {
        while(1==1) {
            try {
                // Create request
                Request currentRequest = this.requestFactory.newRequest("getNetworkName");
                
                // Send request
                if(currentRequest != null) {
                    // Send request to sending thread...
                    this.deviceRequestQueue.put(currentRequest);
                
                    System.out.println("FSM: Sent: " + currentRequest.toString());
                }
                
                // Wait for new response
                Response currentResponse = this.deviceResponseQueue.take();
                
                System.out.println("FSM: Got: "+ currentResponse.toString());
                
                this.sleep(1000);
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }        
    }
    
    
    // Device request queue
    private PriorityBlockingQueue <Request> deviceRequestQueue;
        
    // Device response queue
    private PriorityBlockingQueue <Response> deviceResponseQueue;
        
    // API request queue
    private PriorityBlockingQueue <Request> apiRequestQueue;
        
    // API response queue
    private PriorityBlockingQueue <Response> apiResponseQueue;     
    
    // Request factory
    private RequestFactory requestFactory;
    
    // Statemachine lookup table
    private Hashtable <String, Hashtable> stateMachines;
}
